package com.dhsc.util;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URLEncoder;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Base64;
import java.util.List;

import javax.servlet.http.HttpServletResponse;

import org.springframework.web.multipart.MultipartFile;

import com.dhsc.exception.MyException;

import lombok.extern.slf4j.Slf4j;


/**
 * @ClassName:  FileUtils
 * @Description: 文件工具类
 * @date:   2023年9月20日 下午1:36:05       
 * @author: clong
 * @Copyright:  东海运营
 */
@Slf4j
public class FileUtils {
	
	private FileUtils()
	{

	}
	
	/**
     * @Title: download   
     * @Description: 下载路径
     * @param path
     * @param response void
     * @author: clong
	 * @throws IOException 
     * @date:   2023年9月20日 下午1:35:35
     */
	public static void download(String path, HttpServletResponse response) throws IOException{
		OutputStream toClient = null;
		try (InputStream fis = new BufferedInputStream(new FileInputStream(path))){
			// path是指欲下载的文件的路径。
			File file = new File(path);
			// 取得文件名。
			String filename = file.getName();
			// 以流的形式下载文件。
			byte[] buffer = new byte[fis.available()];
			while(true){
				int read = fis.read(buffer);
				//判断是不是读到了数据流的末尾 ，防止出现死循环。
                if (read == -1) {
                    break;
                }
			}
			// 清空response
			response.reset();
			// 设置response的Header
			response.addHeader("Content-Disposition", 
					"attachment;filename=" + URLEncoder.encode(filename,"UTF-8"));
			response.addHeader("Content-Length", "" + file.length());
			toClient = new BufferedOutputStream(
					response.getOutputStream());
			response.setContentType("application/vnd.ms-excel;charset=gb2312");
			toClient.write(buffer);
			toClient.flush();
			toClient.close();
		} catch (IOException ex) {
			ex.printStackTrace();
		}
   }

	/**
	 * @Title: dirExists   
	 * @Description: 目录是否存在，不存在则创建目录
	 * @param filestr void
	 * @author: clong
	 * @date:   2023年9月20日 下午1:35:16
	 */
	public static void dirExists(String filestr) {
		try {
			File file = new File(filestr);
			if (!file.exists() && !file.isDirectory()) {
				boolean exit = file.mkdirs();
				if(!exit){
	    			log.error(file.getPath()+"目录创建失败");
	    		}
			}
		} catch (Exception e) {
			log.error("判断文件夹是否存在并创建异常", e);
		}
	}

	/**
	 * @Title: deleteFile   
	 * @Description: 删除文件
	 * @param filePath
	 * @throws Exception void
	 * @author: clong
	 * @date:   2023年9月20日 下午1:34:31
	 */
    public static void deleteFile(String filePath){
    	Path pathToFile = Paths.get(filePath);
    	try {
    		// 删除文件或目录
    		if(new File(filePath).exists()) 
        	{
    			 Files.delete(pathToFile);
        	}
        } catch (IOException e) {
        	log.error("删除文件失败=" + filePath, e);
        }
    }
    
    /**
     * @Title: copyFile   
     * @Description: 复制文件
     * @param source
     * @param target
     * @throws IOException void
     * @author: clong
     * @date:   2023年9月20日 下午1:34:09
     */
    public static void copyFile(String source,String target)   throws IOException{
    	File sourceFile = new File(source);
    	File targetFile = new File(target); 
    	try ( FileInputStream input = new FileInputStream(sourceFile);
    			BufferedInputStream inBuff = new BufferedInputStream(input);
    			FileOutputStream output = new FileOutputStream(targetFile);  
                BufferedOutputStream outBuff=new BufferedOutputStream(output);){
            // 缓冲数组   
            byte[] b = new byte[1024 * 5];  
            int len;  
            while ((len =inBuff.read(b)) != -1) {  
                outBuff.write(b, 0, len);  
            }  
            // 刷新此缓冲的输出流   
            outBuff.flush();  
		} catch (IOException ex) {
			ex.printStackTrace();
		}
     } 
    
    /**
     * @Title: encodeBase64File   
     * @Description: 将文件转成base64 字符串
     * @param path
     * @return String
     * @author: clong
     * @date:   2023年9月20日 下午1:33:50
     */
	public static String encodeBase64File(String path){
		String base64 = "";
		FileInputStream inputFile = null;
		try {
			File file = new File(path);
			inputFile = new FileInputStream(file);
			byte[] buffer = new byte[(int)file.length()];
			while(true){
				int read = inputFile.read(buffer);
				//判断是不是读到了数据流的末尾 ，防止出现死循环。
                if (read == -1) {
                    break;
                }
			}
			base64 = Base64.getEncoder().encodeToString(buffer);
		} catch (Exception e) {
			base64 = "";
			log.error("将文件转成base64 字符串出错",e);
			throw new MyException("将文件转成base64 字符串出错", e);
		}finally {
			if(inputFile!=null) {
				try {
					inputFile.close();
				} catch (IOException e) {
					log.error("关闭FileInputStream出错",e);
				}
			}
		}
		return base64;
	}
	
	/**
	 * @Title: judgeFileSize   
	 * @Description: 判断文件大小是否超过上传要求
	 * @param base64
	 * @param size
	 * @return boolean
	 * @author: clong
	 * @date:   2023年9月20日 下午2:03:28
	 */
	public static boolean judgeFileSize(String base64, int size)
	{
		boolean result = false;
		byte[] decodedBytes = Base64.getDecoder().decode(base64);
		// 判断文件流大小是否不超过限制大小
		if (decodedBytes.length <= size * 1024 * 1024) {
			result = true;
		}
		return result;
	}
	
	/**
	 * @Title: judgeFileTypeByWjt   
	 * @Description: 判断文件是否允许上传
	 * @param base64
	 * @param wjlxlist
	 * @return boolean
	 * @author: clong
	 * @date:   2023年9月20日 下午2:38:52
	 */
	public static String judgeFileTypeByWjt(String base64, List<String> types, int size){
		byte[] bytes = Base64.getDecoder().decode(base64);
		
		// 判断文件流大小是否不超过限制大小
		if (!isFileSizeValid(bytes, size * 1024 * 1024)) {
			return "请将上传的文件大小控制在" + size + "M内";
		}
		//判断请求头格式是否符合要求
		boolean qqtFlag=false;
		for(int i=0;i<types.size();i++){
			String type=types.get(i);
			if("jpg".equals(type)||"jpeg".equals(type)){
				if(isJpg(bytes)){
					qqtFlag=true;
					break;
				}
			}else if("png".equals(type)){
				if(isPng(bytes)){
					qqtFlag=true;
					break;
				}
			}else if("pdf".equals(type)){
				if(isPdf(bytes)){
					qqtFlag=true;
					break;
				}
			}else if("docx".equals(type)){
				if(isDocx(bytes)){
					qqtFlag=true;
					break;
				}
			}else if("video".equals(type)){
				if(isVideo(bytes)){
					qqtFlag=true;
					break;
				}
			}else if("mp3".equals(type)){
				if(isMp3(bytes)){
					qqtFlag=true;
					break;
				}
			}else if("mp4".equals(type)){
				if(isMp4(bytes)){
					qqtFlag=true;
					break;
				}
			}
			else if("rar".equals(type)){
				if(isRar(bytes)){
					qqtFlag=true;
					break;
				}
			}else if("doc".equals(type)){
				if(isDoc(bytes)){
					qqtFlag=true;
					break;
				}
			}else if("xls".equals(type)){
				if(isXls(bytes)){
					qqtFlag=true;
					break;
				}
			}else if("ppt".equals(type)){
				if(isPpt(bytes)){
					qqtFlag=true;
					break;
				}
			}else if("xlsx".equals(type)){
				if(isXlsx(bytes)){
					qqtFlag=true;
					break;
				}
			}
		}
		if(!qqtFlag){
			return "文件格式不是"+types.toString();
		}
		return "";
	}
	
	/**
	 * @Title: loadWjlx   
	 * @Description: 文件上传类型
	 * @param type   1.TA管理   代销测试管理页面
	 * @return List<String>
	 * @author: clong
	 * @date:   2023年9月21日 上午10:12:58
	 */
	public static List<String> loadWjlx(int type)
	{
		List<String> lxlist = new ArrayList<>();
		if(type == 1)
		{
			lxlist.add("pdf");
			lxlist.add("doc");
			lxlist.add("xls"); 
			lxlist.add("ppt");
			lxlist.add("docx");
			lxlist.add("xlsx");
			lxlist.add("png");
			lxlist.add("jpg");
			lxlist.add("rar");
		}else if(type == 2)
		{
			lxlist.add("png");
			lxlist.add("jpg");
		}
		return lxlist;
	}
	
	/**
	 * @Title: loadWjdx   
	 * @Description: 文件上传大小限制
	 * @param type 1.TA管理   代销测试管理页面
	 * @return int
	 * @author: clong
	 * @date:   2023年9月21日 上午10:17:56
	 */
	public static int loadWjdx(int type)
	{
		int size = 0;
		if(type == 1)
		{
			size = 30;
		}
		return size;
	}

	/**
	 * 校验文件大小及类型
	 * @param file
	 * @param types 文件格式
	 * @param size 文件大小（单位M）
	 * @throws IOException
	 */
	public static String validFileSizeAndType(MultipartFile file, List<String> types, int size) throws IOException {
		byte[] bytes = file.getBytes();

		// 判断文件流大小是否不超过限制大小
		if (!isFileSizeValid(bytes, size * 1024 * 1024)) {
			return "请将上传的文件大小控制在" + size + "M内";
		}
		//判断请求头格式是否符合要求
		boolean qqtFlag=false;
		for(int i=0;i<types.size();i++){
			String type=types.get(i);
			if("jpg".equals(type)||"jpeg".equals(type)){
				if(isJpg(bytes)){
					qqtFlag=true;
					break;
				}
			}else if("png".equals(type)){
				if(isPng(bytes)){
					qqtFlag=true;
					break;
				}
			}else if("pdf".equals(type)){
				if(isPdf(bytes)){
					qqtFlag=true;
					break;
				}
			}else if("docx".equals(type)){
				if(isDocx(bytes)){
					qqtFlag=true;
					break;
				}
			}else if("video".equals(type)){
				if(isVideo(bytes)){
					qqtFlag=true;
					break;
				}
			}else if("mp3".equals(type)){
				if(isMp3(bytes)){
					qqtFlag=true;
					break;
				}
			}else if("mp4".equals(type)){
				if(isMp4(bytes)){
					qqtFlag=true;
					break;
				}
			}
			else if("rar".equals(type)){
				if(isRar(bytes)){
					qqtFlag=true;
					break;
				}
			}else if("doc".equals(type)){
				if(isDoc(bytes)){
					qqtFlag=true;
					break;
				}
			}else if("xls".equals(type)){
				if(isXls(bytes)){
					qqtFlag=true;
					break;
				}
			}else if("ppt".equals(type)){
				if(isPpt(bytes)){
					qqtFlag=true;
					break;
				}
			}else if("xlsx".equals(type)){
				if(isXlsx(bytes)){
					qqtFlag=true;
					break;
				}
			}
		}
		if(!qqtFlag){
			return "文件格式不是"+types.toString();
		}
		return "";
	}

	// 检查是否为JPG或JPEG 图片
	private static boolean isJpg(byte[] buffer) {
		// JPG、JPEG 文件头的魔术数字是 0xFFD8
		return buffer.length >= 2 && buffer[0] == (byte) 0xFF && buffer[1] == (byte) 0xD8;
	}

	// 检查是否为 rar 格式
	private static boolean isRar(byte[] buffer) {
		// RAR 文件头的魔术数字是 "Rar!"（0x52 0x61 0x72 0x21）
		byte[] rarMagicNumber = {(byte) 0x52, (byte) 0x61, (byte) 0x72, (byte) 0x21};
		if (buffer.length < rarMagicNumber.length) {
			return false;
		}

		// 获取前几个字节转成十六进制，判断
		byte[] newBuffer = new byte[rarMagicNumber.length];
		System.arraycopy(buffer, 0, newBuffer, 0, rarMagicNumber.length);

		return Arrays.equals(newBuffer, rarMagicNumber);
	}

	private static boolean isDoc(byte[] buffer) {
		// DOC 文件头的魔术数字通常是 "D0 CF 11 E0 A1 B1 1A E1" 的16进制值
		byte[] docMagicNumber = {(byte) 0xD0, (byte) 0xCF, (byte) 0x11, (byte) 0xE0, (byte) 0xA1, (byte) 0xB1, (byte) 0x1A, (byte) 0xE1};

		if (buffer.length < docMagicNumber.length) {
			return false;
		}
		for (int i = 0; i < docMagicNumber.length; i++) {
			if (buffer[i] != docMagicNumber[i]) {
				return false;
			}
		}
		return true;
	}

	private static boolean isXlsx(byte[] buffer) {
		// XLSX 文件头的魔数通常是 "50 4B 03 04" 的16进制值
		byte[] xlsxMagicNumber = {(byte) 0x50, (byte) 0x4B, (byte) 0x03, (byte) 0x04};
		if (buffer.length < xlsxMagicNumber.length) {
			return false;
		}
		for (int i = 0; i < xlsxMagicNumber.length; i++) {
			if (buffer[i] != xlsxMagicNumber[i]) {
				return false;
			}
		}
		return true;
	}


	private static boolean isPpt(byte[] buffer) {
		// PPT 文件头的魔术数字通常是 "D0 CF 11 E0 A1 B1 1A E1" 的16进制值
		byte[] pptMagicNumber = {(byte) 0xD0, (byte) 0xCF, (byte) 0x11, (byte) 0xE0, (byte) 0xA1, (byte) 0xB1, (byte) 0x1A, (byte) 0xE1};

		if (buffer.length < pptMagicNumber.length) {
			return false;
		}
		for (int i = 0; i < pptMagicNumber.length; i++) {
			if (buffer[i] != pptMagicNumber[i]) {
				return false;
			}
		}
		return true;
	}


	private static boolean isXls(byte[] buffer) {
		// XLS 文件头的魔术数字通常是 "D0 CF 11 E0 A1 B1 1A E1" 的16进制值
		byte[] xlsMagicNumber = {(byte) 0xD0, (byte) 0xCF, (byte) 0x11, (byte) 0xE0, (byte) 0xA1, (byte) 0xB1, (byte) 0x1A, (byte) 0xE1};
		if (buffer.length < xlsMagicNumber.length) {
			return false;
		}
		for (int i = 0; i < xlsMagicNumber.length; i++) {
			if (buffer[i] != xlsMagicNumber[i]) {
				return false;
			}
		}
		return true;
	}



	// 检查是否为 PNG 图片
	private static boolean isPng(byte[] buffer) {
		// PNG 文件头的魔术数字是 "PNG"（0x89 50 4E 47 0D 0A 1A 0A）
		byte[] pngMagicNumber = {(byte) 0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A};
		if(buffer.length < pngMagicNumber.length){
			return false;
		}
		// 获取前几个字节转成十六进制，判断
		byte[] newbuffer = new byte[pngMagicNumber.length];
		System.arraycopy(buffer, 0, newbuffer, 0, pngMagicNumber.length);
		return Arrays.equals(newbuffer, pngMagicNumber);
	}

	// 检查是否为 PDF 文档
	private static boolean isPdf(byte[] buffer) {
		// PDF 文件头的魔术数字是 "%PDF"
		byte[] pdfMagicNumber = {(byte) 0x25, (byte) 0x50, (byte) 0x44, (byte) 0x46};
		if(buffer.length < pdfMagicNumber.length){
			return false;
		}
		// 获取前几个字节转成十六进制，判断
		byte[] newbuffer = new byte[pdfMagicNumber.length];
		System.arraycopy(buffer, 0, newbuffer, 0, pdfMagicNumber.length);
		return Arrays.equals(newbuffer, pdfMagicNumber);
	}

	// 检查文件头是否为 DOCX 文档
	private static boolean isDocx(byte[] buffer) {
		// DOCX 文件头的魔术数字是 "PK"（0x50 0x4B）
		return buffer.length >= 2 && buffer[0] == 0x50 && buffer[1] == 0x4B;
	}

	// 检查文件头是否为 MP4 文件
	private static boolean isVideo(byte[] buffer) {
		boolean result = false;
		// 根据不同的视频格式魔术数字进行判断
		if (isMp4(buffer) || isMp3(buffer) || isWAV(buffer)) {
			result = true;
		}
		return result;
	}
	// 检查是否为 WAV 格式
	public static boolean isWAV(byte[] buffer) {
		byte[] wavMagicNumber = {(byte) 0x52, (byte) 0x49, (byte) 0x46, (byte) 0x46}; // WAV 的特征字节序列 "RIFF"
		int magicNumberOffset = 0;

		return buffer.length >= magicNumberOffset + wavMagicNumber.length &&
				Arrays.equals(Arrays.copyOfRange(buffer, magicNumberOffset, magicNumberOffset + 4), wavMagicNumber);
	}


	// 检查是否为 MP3 格式
	private static boolean isMp3(byte[] buffer) {
		byte[] mp3MagicNumber = {(byte) 0xFF, (byte) 0xE0}; // MP3的同步字（只检查前两个字节）
		int magicNumberOffset = 0;
		// 检查buffer是否足够长来包含同步字
		if (buffer.length < magicNumberOffset + mp3MagicNumber.length) {
			return false;
		}
		// 检查buffer的开头是否包含同步字
		for (int i = 0; i <= buffer.length - mp3MagicNumber.length; i++) {
			if (Arrays.equals(Arrays.copyOfRange(buffer, i, i + mp3MagicNumber.length), mp3MagicNumber)) {
				// 找到同步字，可能是MP3文件
				return true;
			}
		}
		// 没有找到同步字
		return false;
	}

	// 检查是否为 MP4 格式
	private static boolean isMp4(byte[] buffer) {
		byte[] mp4MagicNumber = {(byte) 0x66, (byte) 0x74, (byte) 0x79, (byte) 0x70};
		int magicNumberOffset = 0; // 修正偏移量，MP4魔术数字位于文件开始处
		// 检查buffer的长度是否足够包含魔术数字
		if (buffer.length < magicNumberOffset + mp4MagicNumber.length) {
			return false; // 如果不够长，则不是MP4文件
		}
		// 检查buffer的开头是否包含魔术数字
		return Arrays.equals(Arrays.copyOfRange(buffer, magicNumberOffset, magicNumberOffset + mp4MagicNumber.length), mp4MagicNumber);
	}

	// 判断文件流大小是否不超过指定限制（以字节为单位）
	private static boolean isFileSizeValid(byte[] buffer, long maxSizeBytes) {
		return buffer.length <= maxSizeBytes;
	}

	/**
	 * @Title: base64ToFile   
	 * @Description: base64转文件
	 * @param base64
	 * @param fileName
	 * @param path
	 * @return String
	 * @author: clong
	 * @date:   2024年1月31日 下午2:41:51
	 */
	public static String base64ToFile(String base64,String fileName,String path) {
		String filepath ="";
		FileOutputStream fos = null;
		try {
			//创建文件目录
			File dir = new File(path);
			if(!dir.exists()&&!dir.isDirectory()) {
				dir.mkdirs();
			}
			byte[] bytes = Base64.getDecoder().decode(base64);
			filepath = path + "/" + fileName;
			File file = new File(filepath);
			if (!file.exists()) {
				file.createNewFile();
			}
			fos = new FileOutputStream(file);
			fos.write(bytes);
		} catch (Exception e) {
			filepath = "";
			log.error("base64转文件出错",e);
		} finally {
			if(fos!=null) {
				try {
					fos.close();
				} catch (IOException e) {
					log.error("关闭FileOutputStream出错",e);
				}
			}
		}
		return filepath;
	}
}
